package datasource

import (
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/yanggit123/tool/module"
	"github.com/FZambia/sentinel"
	"github.com/gomodule/redigo/redis"
	"github.com/mna/redisc"
	"strconv"
	"time"
)

var redisConn *redisInfo

type redisInfo struct {
	Conn      *redis.Pool
	Cluster   *redisc.Cluster
	IsCluster bool
}

var RedisDB redisDB

type redisDB int

// Setup Initialize the Redis instance
func EnableRedis(conf module.RedisConf) error {
	conn := &redis.Pool{
		MaxIdle:     conf.MaxIdle,
		MaxActive:   conf.MaxActive,
		IdleTimeout: conf.IdleTimeout,
		Dial: func() (redis.Conn, error) {

			c, err := redis.Dial("tcp", conf.Host, redis.DialDatabase(conf.DB))
			if err != nil {
				return nil, err
			}
			if conf.Password != "" {
				if _, err := c.Do("AUTH", conf.Password); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
	redisConn = &redisInfo{
		Conn:      conn,
		IsCluster: false,
	}
	return nil
}
func EnableSentinelRedis(conf module.RedisConf) error {
	if conf.MasterName == "" {
		conf.MasterName = "mymaster"
	}
	sntnl := &sentinel.Sentinel{
		Addrs:      conf.SentinelAddr,
		MasterName: conf.MasterName,
		Dial: func(addr string) (redis.Conn, error) {
			timeout := 500 * time.Millisecond
			c, err := redis.Dial("tcp", addr, redis.DialDatabase(conf.DB), redis.DialConnectTimeout(timeout),
				redis.DialReadTimeout(timeout),
				redis.DialWriteTimeout(timeout))
			if err != nil {
				return nil, err
			}
			return c, nil
		},
	}
	conn := &redis.Pool{
		MaxIdle:     conf.MaxIdle,
		MaxActive:   conf.MaxActive,
		IdleTimeout: conf.IdleTimeout,
		Wait:        true,
		Dial: func() (redis.Conn, error) {
			masterAddr, err := sntnl.MasterAddr()
			if err != nil {
				return nil, err
			}
			c, err := redis.Dial("tcp", masterAddr)
			if err != nil {
				return nil, err
			}
			if conf.Password != "" {
				if _, err := c.Do("AUTH", conf.Password); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			if !sentinel.TestRole(c, "master") {
				return errors.New("Role check failed")
			} else {
				return nil
			}
		},
	}
	redisConn = &redisInfo{
		Conn:      conn,
		IsCluster: false,
	}
	return nil
}

// 集群模式下不允许设置db
func EnableClusterRedis(conf module.RedisConf) error {
	cluster := &redisc.Cluster{
		StartupNodes: conf.ClusterAddr,
		CreatePool: func(addr string, opts ...redis.DialOption) (*redis.Pool, error) {
			return &redis.Pool{
				MaxIdle:     conf.MaxIdle,
				MaxActive:   conf.MaxActive,
				IdleTimeout: conf.IdleTimeout,
				Dial: func() (redis.Conn, error) {
					return redis.Dial("tcp", addr, redis.DialPassword(conf.Password))
				},
				TestOnBorrow: func(c redis.Conn, t time.Time) error {
					_, err := c.Do("PING")
					return err
				},
			}, nil
		},
	}
	err := cluster.Refresh()
	if err != nil {
		return err
	}
	// grab a connection from the pool
	redisConn = &redisInfo{
		Cluster:   cluster,
		IsCluster: true,
	}
	return nil
}
func (redisInfo *redisInfo) Get() redis.Conn {
	if redisInfo.IsCluster {
		return redisInfo.Cluster.Get()
	} else {
		return redisInfo.Conn.Get()
	}
}

// 设置一个slice或者struct
func (r redisDB) Set(key string, data interface{}, time int) error {
	conn := redisConn.Get()
	defer conn.Close()

	value, err := json.Marshal(data)
	if err != nil {
		return err
	}
	if time != 0 {
		_, err = conn.Do("SET", key, value, "EX", time)
	} else {
		_, err = conn.Do("SET", key, value)
	}
	return err
}

// 获取一个slice或者struct返回的bytes
func (r redisDB) Get(key string) ([]byte, error) {
	conn := redisConn.Get()
	defer conn.Close()

	reply, err := redis.Bytes(conn.Do("GET", key))
	if err != nil {
		return nil, err
	}

	return reply, nil
}

// 通过此方法可直接反序列化对象  设置可通过Set方法即可
func (r redisDB) GetStructOrSlice(key string, value interface{}) error {
	conn := redisConn.Get()
	defer conn.Close()

	reply, err := redis.Bytes(conn.Do("GET", key))
	if err != nil {
		return err
	}
	err = json.Unmarshal(reply, value)
	if err != nil {
		return err
	}
	return nil
}
func (r redisDB) SetString(key string, value string, time int) error {
	conn := redisConn.Get()
	defer conn.Close()
	var err error

	if time != 0 {
		_, err = conn.Do("SET", key, value, "EX", time)
	} else {
		_, err = conn.Do("SET", key, value)
	}
	return err
}
func (r redisDB) GetString(key string) (string, error) {
	conn := redisConn.Get()
	defer conn.Close()

	return redis.String(conn.Do("GET", key))
}
func (r redisDB) SetBool(key string, value bool, time int) error {
	conn := redisConn.Get()
	defer conn.Close()

	var err error
	if time != 0 {
		_, err = conn.Do("SET", key, value, "EX", time)
	} else {
		_, err = conn.Do("SET", key, value)
	}
	return err
}
func (r redisDB) GetBool(key string) (bool, error) {
	conn := redisConn.Get()
	defer conn.Close()

	return redis.Bool(conn.Do("GET", key))
}
func (r redisDB) SetInt(key string, data int, time int) error {
	conn := redisConn.Get()
	defer conn.Close()

	var err error
	if time != 0 {
		_, err = conn.Do("SET", key, data, "EX", time)
	} else {
		_, err = conn.Do("SET", key, data)
	}
	return err
}
func (r redisDB) GetInt(key string) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()

	return redis.Int(conn.Do("GET", key))
}

// value只能是0和1
func (r redisDB) SetBit(key string, offset int, value int) error {
	conn := redisConn.Get()
	defer conn.Close()

	var err error
	_, err = conn.Do("SETBIT", key, offset, value)
	return err
}
func (r redisDB) GetBit(key string, offset int) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()

	return redis.Int(conn.Do("GETBIT", key, offset))
}

// 查看有多少位设置成了1
func (r redisDB) BitCount(key string) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()

	return redis.Int(conn.Do("BITCOUNT", key))
}

// 分布式锁所需 value请设置每个客户端唯一值
/*
如果 SETNX 返回1，说明该进程获得锁，
如果 SETNX 返回0，说明其他进程已经获得了锁，进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作，以获得锁。
*/
func (r redisDB) SetNX(key string, value string, time int) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()
	var nx int
	var err error

	if time != 0 {
		nx, err = redis.Int(conn.Do("SETNX", key, value))
		if err != nil {
			return nx, err
		}
		if nx == 0 {
			return 0, nil
		}
		_, err = conn.Do("expire", key, time)
	} else {
		nx, err = redis.Int(conn.Do("SETNX", key, value))
	}
	return nx, err
}

// Exists check a key
func (r redisDB) Exists(key string) bool {
	conn := redisConn.Get()
	defer conn.Close()

	exists, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		return false
	}

	return exists
}

// 返回1成功
func (r redisDB) Expire(key string, time int) bool {
	conn := redisConn.Get()
	defer conn.Close()

	isSuccess, _ := redis.Int(conn.Do("expire", key, time))
	return isSuccess == 1
}
func (r redisDB) Incre(key string) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()

	value, err := redis.Int(conn.Do("INCR", key))
	if err != nil {
		return 0, err
	}
	return value, nil
}

// Delete delete a kye
func (r redisDB) Delete(key string) (bool, error) {
	conn := redisConn.Get()
	defer conn.Close()

	return redis.Bool(conn.Do("DEL", key))
}

// LikeDeletes batch delete
func (r redisDB) LikeDeletes(key string) error {
	conn := redisConn.Get()
	defer conn.Close()

	keys, err := redis.Strings(conn.Do("KEYS", "*"+key+"*"))
	if err != nil {
		return err
	}
	for _, key := range keys {
		_, err = r.Delete(key)
		if err != nil {
			return err
		}
	}

	return nil
}

// 可实现滑动窗口限流
func (r redisDB) ZAdd(key string, score int, value interface{}) error {
	conn := redisConn.Get()
	defer conn.Close()
	_, err := conn.Do("ZADD", key, score, value)
	return err
}

func (r redisDB) ZRank(key string, value int) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()
	rank, err := redis.Int(conn.Do("ZRANK", key, strconv.Itoa(value)))
	if err != nil {
		return 0, err
	}
	return rank, nil
}

func (r redisDB) ZRange(key string, offset int) ([]int, error) {
	conn := redisConn.Get()
	defer conn.Close()
	ranks, err := redis.Ints(conn.Do("ZRANGE", key, 0, offset))
	if err != nil {
		return nil, err
	}

	return ranks, nil
}

// options key min max [WITHSCORES] [LIMIT offset count]
func (r redisDB) ZRangeByScore(options ...interface{}) ([]interface{}, error) {
	conn := redisConn.Get()
	defer conn.Close()
	ranks, err := redis.Values(conn.Do("ZRANGEBYSCORE", options...))
	if err != nil {
		return nil, err
	}

	return ranks, nil
}

// 随机返回元素
func (r redisDB) ZRandMember(key string) (string, error) {
	conn := redisConn.Get()
	defer conn.Close()
	str, err := redis.String(conn.Do("ZRANDMEMBER", key))
	if err != nil {
		return "", err
	}

	return str, nil
}

// 增加计数
func (r redisDB) INCRBY(key string, increment int) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()
	newNum, err := redis.Int(conn.Do("INCRBY", key, increment))
	if err != nil {
		return 0, err
	}
	return newNum, nil
}

// 获取计数
func (r redisDB) GetCounter(key string) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()
	count, err := redis.Int(conn.Do("GET", key))
	if err != nil {
		return 0, err
	}
	return count, nil
}

// 初始化计数器
func (r redisDB) InitCounter(key string) error {
	conn := redisConn.Get()
	defer conn.Close()
	_, err := conn.Do("SET", key, 0)
	if err != nil {
		return err
	}
	return nil
}

var ErrCounterHasBeenZero = errors.New("the counter has been 0")

// 减少计数
func (r redisDB) DECRBY(key string, decrement int) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()
	count, err := r.GetCounter(key)
	if err != nil {
		return 0, err
	}
	if count == 0 {
		return 0, ErrCounterHasBeenZero
	}
	newNum, err := redis.Int(conn.Do("DECRBY", key, decrement))
	if err != nil {
		return 0, err
	}
	return newNum, nil
}

// 根据pattern获取keys(可能成为性能瓶颈)
func (r redisDB) Keys(pattern string) ([]string, error) {
	conn := redisConn.Get()
	defer conn.Close()
	return redis.Strings(conn.Do("KEYS", pattern))
}

// lua脚本执行 key不会自动添加
func (r redisDB) LuaScript(keyCount int, keysAndArgs []interface{}, lua string) (interface{}, error) {
	conn := redisConn.Get()
	defer conn.Close()
	script := redis.NewScript(keyCount, lua)
	return script.Do(conn, keysAndArgs...)
}

// 命令操作 key不会自动添加
func (r redisDB) Operator(command string, value ...interface{}) (interface{}, error) {
	conn := redisConn.Get()
	defer conn.Close()
	return conn.Do(command, value...)
}
func (r redisDB) TTL(key string) {
	conn := redisConn.Get()
	defer conn.Close()

	ttl, err := redis.Int64(conn.Do("ttl", key))
	fmt.Println(ttl, err)
}

// 集合
func (r redisDB) SAdd(key interface{}, value ...interface{}) error {
	values := []interface{}{key}
	values = append(values, value...)
	conn := redisConn.Get()
	defer conn.Close()
	_, err := conn.Do("SADD", values...)
	return err
}

// 获取集合所有元素
func (r redisDB) SMembers(key interface{}) ([]interface{}, error) {
	conn := redisConn.Get()
	defer conn.Close()
	return redis.Values(conn.Do("SMEMBERS", key))
}

// 判断是否集合成员
func (r redisDB) SisMember(key interface{}, value interface{}) (bool, error) {
	conn := redisConn.Get()
	defer conn.Close()
	b, err := redis.Bool(conn.Do("SISMEMBER", key, value))
	return b, err
}

// 获取集合中随机的多个数据
func (r redisDB) SRandMember(key interface{}, count int) ([][]byte, error) {
	values := []interface{}{key}
	if count != 0 {
		values = append(values, count)
	}

	conn := redisConn.Get()
	defer conn.Close()
	if count == 0 {
		data, err := redis.Bytes(conn.Do("SRANDMEMBER", values...))
		return [][]byte{data}, err
	} else {
		data, err := redis.ByteSlices(conn.Do("SRANDMEMBER", values...))
		return data, err
	}

}

// 获取并移除集合中随机的多个数据
func (r redisDB) SPop(key interface{}, count int) ([]interface{}, error) {
	values := []interface{}{key}
	if count != 0 {
		values = append(values, count)
	}
	conn := redisConn.Get()
	defer conn.Close()
	data, err := redis.Values(conn.Do("SPOP", values...))
	return data, err
}

// 移除集合中的元素
func (r redisDB) SRem(key interface{}, value ...interface{}) error {
	values := []interface{}{key}
	values = append(values, value...)
	conn := redisConn.Get()
	defer conn.Close()
	_, err := conn.Do("SREM", values...)
	return err
}
func (r redisDB) RPop(key interface{}) (interface{}, error) {
	conn := redisConn.Get()
	defer conn.Close()
	data, err := redis.Bytes(conn.Do("RPOP", key))
	return data, err
}
func (r redisDB) LPush(key,value interface{}) (error) {
    values := []interface{}{key}
	values = append(values, value)
	conn := redisConn.Get()
	defer conn.Close()
	_, err := conn.Do("LPUSH", values...)
	return err
}
func (r redisDB) LLen(key interface{}) (int,error) {
	conn := redisConn.Get()
	defer conn.Close()
	data, err :=redis.Int(conn.Do("LLEN",key))
	return data, err
}
// 返回集合中元素数量
func (r redisDB) SCard(key interface{}) (int, error) {
	conn := redisConn.Get()
	defer conn.Close()
	data, err := redis.Int(conn.Do("SCARD", key))
	return data, err
}
